package othello.server.service;

import java.util.logging.Level;
import java.util.logging.Logger;

import othello.client.service.Lounge;
import othello.server.memcacheutil.LockFailed;
import othello.server.memcacheutil.LoungeStateCacher;
import othello.shared.model.GameStep;
import othello.shared.model.StateAtLounge;

public class LoungeImpl implements Lounge {

	private static final String MESSAGE_CHECK_STATE = "state check for \"{0}\"";
	private static final String MESSAGE_ENTRY_NEW = "new entried for \"{0}\"";
	private static final String MESSAGE_LOCK_FAILED = "memcache lock failed on session \"{0}\"";
	private static final Logger LOGGER = Logger.getLogger(LoungeImpl.class
			.getName());

	/**
	 * <p>
	 * 入室処理を行います。
	 * </p>
	 * <ul>
	 * <li>先に入室している相手がいなければ「待機中」で入室状態を登録</li>
	 * <li>先に入室している相手がいる場合、相手の入室状態を「認証待ち」に更新</li>
	 * </ul>
	 */
	public String entry(String sessionId) {
		try {
			// 自分の状態により新規入室 or マッチング依頼確認
			StateAtLounge myEntry = LoungeStateCacher.get(sessionId);
			if (myEntry == null) {
				return entryNew(sessionId);

			} else {
				return checkState(myEntry);
			}

		} catch (LockFailed e) {
			// 登録・更新に失敗した場合、ログ出力して一旦処理終了
			LOGGER.log(Level.WARNING, MESSAGE_LOCK_FAILED, e);
			return null;
		}
	}

	/**
	 * <p>
	 * 新規登録時の処理。
	 * </p>
	 * <p>
	 * 次の処理を実行する。
	 * <ul>
	 * <li>先に入室している相手とのマッチング</li>
	 * <li>マッチング出来た場合、生成されたIDでゲーム盤を初期登録</li>
	 * <li>マッチングの有無に関わらず状態を登録</li>
	 * </ul>
	 * </p>
	 * 
	 * @param sessionId
	 * @throws LockFailed
	 */
	private String entryNew(String sessionId) throws LockFailed {
		LOGGER.log(Level.CONFIG, MESSAGE_ENTRY_NEW, sessionId);

		StateAtLounge myEntry = new StateAtLounge();
		myEntry.setSessionId(sessionId);
		myEntry.setSide(GameStep.Side.White);

		// 新規入室、先に入室している相手とのマッチングの試行
		// 後から入室した側が黒石（先行）を取る
		StateAtLounge partnerState = LoungeStateCacher.matchAnyone(sessionId);
		if (partnerState != null) {
			myEntry.setPartnerId(partnerState.getSessionId());
			myEntry.setGameBoardId(partnerState.getGameBoardId());
			myEntry.setSide(GameStep.Side.Black);
		}

		// マッチのありなしにかかわらず状態の登録
		LoungeStateCacher.entry(sessionId, myEntry);

		return myEntry.getGameBoardId();

	}

	/**
	 * 既に待機状態の場合の処理 相手からのマッチング依頼を確認
	 * 
	 * @param myEntry
	 * @throws LockFailed
	 */
	private String checkState(StateAtLounge myEntry) throws LockFailed {
		LOGGER.log(Level.CONFIG, MESSAGE_CHECK_STATE, myEntry.getSessionId());

		// 入室済み、相手からのマッチが登録されたか確認
		if (myEntry.getGameBoardId() != null) {
			// マッチあり、状態の削除
			return myEntry.getGameBoardId();

		} else {
			// TODO マッチなし。今後必要に応じて最終更新日時の更新処理を行う
			return null;
		}

	}

	/**
	 * <p>
	 * 指定されたセッションID、相手側の登録を解除する。
	 * </p>
	 * 
	 * @param sessionId
	 * @return 削除された自分側の登録情報
	 */
	public StateAtLounge reset(String sessionId) {
		LOGGER.entering(this.getClass().getName(), "reset", sessionId);
		// 自分と対戦相手のセッションの登録を削除
		StateAtLounge state = LoungeStateCacher.get(sessionId);
		if (state != null) {
			LoungeStateCacher.remove(sessionId);

			if (state.getPartnerId() != null) {
				LoungeStateCacher.remove(state.getPartnerId());
			}
		}
		LOGGER.exiting(this.getClass().getName(), "reset", state);
		return state;
	}

}
